﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;

namespace Core
{
    public class X
    {
        private C _a { get; set; }
        private C[] _fs;                                        //List field cua table hoac view ma action do dung
        private readonly Dictionary<string, object> _ip;        //Input cliet truyen vao
        private readonly List<C> _p = new List<C>();            //List field _c service push _c: fieldName; value: type
        private readonly List<C> _c = new List<C>();            //List field _c service push _c key: fieldName; value: value, type
        private readonly List<C> _d = new List<C>();            //List field data client truyen vao key: fieldName; value: value, type
        private string _f;                                      //Chuoi field client truyen vao
        private readonly R _r = new R { _s = 1 };               //Request gui ve cho client
        private string _sql;                                    //Lenh sql tim duoc
        private List<string> _sqlL = new List<string>();                                    //Lenh sql tim duoc
        public static string _cs = "";                          //Conection string
        public readonly Random _rd = new Random();
        public D d = new D();
        public static void Init(string cs)
        {
            _cs = cs;
        }
        public X(object input)
        {
            _ip = (Dictionary<string, object>)input;
        }
        public X R()
        {
            if (_r._e) return this; _r._s = 1; _r._d = ""; return this;
        }
        public void LR(int s, int e, object d)
        {        // Lookup random
            if (true) { }
        }
        public bool CA()
        {  // Kiem tra va lay action
            if (_r._e) return false;
            if (_ip.ContainsKey("a"))
            {
                var a = _ip["a"] as string;
                if (a != null && d._a.ContainsKey(a)) return true;
            }
            return false;
        }
        public X A()
        {  // Kiem tra va lay action
            if (_r._e) return this;
            if (_ip.ContainsKey("a"))
            {
                var a = _ip["a"] as string;
                if (a != null && d._a.ContainsKey(a))
                {
                    _a = d._a[a];
                    _fs = _a.T[0][0] == 'G' ? D._fd[int.Parse(_a.T[2])] : D._fd[int.Parse(_a.T[1])];
                    return this;
                }
                _r._s = 0; _r._d = _E.E10001.G(); return this;
            }
            _r._s = 0; _r._d = _E.E10002.G(); return this;
        }
        public X Pc(string value)
        {       // Push Field
            if (_r._e) return this;
            var fi = L(_fs, value, 0);
            if (fi != "") _p.Add(new C { T = new[] { value, fi } });
            else { _r._s = 0; _r._d = _E.E10005.G(); }
            return this;
        }
        public X _CR()
        {       //Check _c
            if (_r._e) return this;
            if (_ip.ContainsKey("c"))
            {
                var c = _ip["c"] as Dictionary<string, object>;
                if (c != null)
                    foreach (var i in c)
                        if (!L(_p, i.Key, 0))
                        {     //Kiem tra neu field _c truyen vao khong dung giong voi tap field _c cua action thi bao loi
                            _r._s = 0; _r._d = _E.E10006.G(); break;
                        }
            }
            return this;
        }
        public X _CF()
        {        //Kiem tra field trong obj.data vaf obj.field
            if (_r._e) return this;
            if (_ip.ContainsKey("d"))
            {       //Check obj._d
                var da = _ip["d"] as Dictionary<string, object>;
                if (da != null)
                {
                    foreach (var d in da)
                        if (L(_fs, d.Key, 0) == "") { _r._s = 0; _r._d = _E.E10007.G(); }
                    if (!V(da)) { _r._s = 0; _r._d = _E.E10010.G(); }     //Validate
                }
            }
            if (!_ip.ContainsKey("_f") || _ip["_f"] + "" == "") _f = "*";
            else _f = _ip["_f"].ToString();          //Note: Trong service thì nên split Field để kiểm tra.
            return this;
        }
        public X _CG()
        {
            if (!_ip.ContainsKey("d") || !((Dictionary<string, object>)_ip["d"]).ContainsKey("count"))
            {
                _r._s = 0; _r._d = _E.E10006.G();
            }
            return this;
        }
        public string L(C[] f, string k, int j)
        {       // Lookup Field: param key
            for (int i = 0; i < f.Length; i++) if (f[i].T[7] == k) return f[i].T[j]; return "";
        }
        public string LF(List<C> f, string k, int j)
        {
            for (int i = 0; i < f.Count; i++) if (f[i].T[7] == k) return f[i].T[j]; return "";
        }
        public bool L(List<C> f, string k, int j)
        {
            for (int i = 0; i < f.Count; i++) if (f[i].T[j] == k) return true; return false;
        }
        public string[] LI(List<C> f)
        {
            string[] r = { "", "" };
            for (int i = 0; i < f.Count; i++)
                if (!f[i].T[0].Equals("Id"))
                {////int: 1; float: 2; string: 3; date: 4; bool: 5, bit:9
                    if (r[0] == "") r[0] += f[i].T[0]; else r[0] += "," + f[i].T[0];
                    string t = (f[i].T[1][0] == '0' || f[i].T[1][0] == '1' || f[i].T[1][0] == '2') ? ((f[i].T[2] == null || f[i].T[2].Length < 1) ? "null" : f[i].T[2]) : ("N'" + f[i].T[2] + "'");
                    if (f[i].T[1][0] == '9' || f[i].T[1][0] == '3')//bit && date
                        t = (f[i].T[2] == null || f[i].T[2].Length < 1) ? "null" : "'" + f[i].T[2] + "'";
                    if (t.Length > 0) if (r[1] == "") r[1] += t; else r[1] += "," + t;
                }
            return r;
        }
        public string LW(List<C> f)
        {       // loopup for update sql
            string r = "";
            for (int i = 0; i < f.Count; i++)
            {
                var x = "";
                if (f[i].T[2].Trim().IndexOf("$x") != -1) x = f[i].T[2].Replace("$x", f[i].T[0]);
                else x = f[i].T[0] + "=" + ((f[i].T[1][0] == '0' || f[i].T[1][0] == '1') ? f[i].T[2] : ("N'" + f[i].T[2] + "'"));
                if (r == "") r += " WHERE " + x; else r += " AND " + x;
            }
            return r;
        }
        public string LU(List<C> f)
        {       // loopup for update sql
            string r = "";
            for (int i = 0; i < f.Count; i++)
                if (!f[i].T[0].Equals("Id"))
                {////int: 1; float: 2; string: 3; date: 4; bool: 5, bit:9
                    string t = (f[i].T[1][0] == '0' || f[i].T[1][0] == '1' || f[i].T[1][0] == '2') ? ((f[i].T[2] == null || f[i].T[2].Length < 1) ? "" : f[i].T[2]) : ("N'" + f[i].T[2] + "'");
                    if (f[i].T[1][0] == '9' || f[i].T[1][0] == '3')//bit && date
                        t = (f[i].T[2] == null || f[i].T[2].Length < 1) ? "" : "'" + f[i].T[2] + "'";
                    if (r == "") r += (t.Length > 0) ? (f[i].T[0] + "=" + t) : ""; else r += (t.Length > 0) ? ("," + f[i].T[0] + "=" + t) : "";
                }
            return r;
        }
        public X L()
        {
            if (_r._e) return this;
            var cd = _ip["c"] as Dictionary<string, object>;
            var da = _ip["d"] as Dictionary<string, object>;
            var lfi = _a.T[0][0] == 'G' ? D._fd[int.Parse(_a.T[2])] : D._fd[int.Parse(_a.T[1])];
            if (cd != null) foreach (var c in cd)
                    _c.Add(new C { T = new[] { c.Key, L(lfi, c.Key, 0), c.Value + "" } });
            if (da != null) foreach (var dv in da)
                    _d.Add(new C { T = new[] { dv.Key, L(lfi, dv.Key, 0), dv.Value + "" } });
            return this;
        }
        public bool V(object data)
        {
            return true;
        }
        public R G()
        {
            return _r;
        }
        public X EX()
        {
            if (_r._e) return this;
            switch (_a.T[0][0])
            {
                case 'G': GD(_sql); break;
                case 'P': GDP(); break;
                case 'I':
                case 'U':
                case 'D': IoU(_sql); break;
                default: break;
            }
            return this;
        }
        public X S()
        {
            if (_r._e) return this;
            var v = _a.T[0][0] == 'G' ? _a.T[4] : _a.T[3];
            switch (_a.T[0][0])
            {
                case 'G': G(v); break;
                //case 'P': GDP(); break;
                case 'I': I(v); break;
                case 'U': U(v); break;
                case 'D': R(v); break;
                default: break;
            }
            return this;
        }
        void GD(string sql)
        {       //Get data
            var result = new DataTable();
            using (var con = new SqlConnection(_cs))
            {
                var cmd = new SqlCommand(sql, con);
                cmd.Connection.Open();
                var adap = new SqlDataAdapter(cmd);
                try
                {
                    adap.Fill(result);
                    var list = new List<object[]>();
                    for (var m = 0; m < result.Rows.Count; m++) list.Add(result.Rows[m].ItemArray);
                    _r._d = list; _r._t = result.Rows.Count;
                }
                catch (Exception) { _r._s = 0; _r._d = _E.E10009.G(); }
                finally { adap.Dispose(); cmd.Dispose(); con.Close(); }
            }
        }
        public void GDP()
        {
            string se = _ip.ContainsKey("se") ? _ip["se"] + "" : " Id desc";
            string cl = _ip.ContainsKey("cl") ? _ip["cl"] + "" : "*";
            string f = _ip.ContainsKey("f") ? _ip["f"] + "" : "";
            int mr = _ip.ContainsKey("mr") ? (int)_ip["mr"] : 25;
            int si = _ip.ContainsKey("si") ? (int)_ip["si"] : 1;
            string table = _a.T[0][0] == 'P' ? _a.T[4] : _a.T[3];
            //---------------------------------------------------------------
            SqlCommand cmd = new SqlCommand();
            cmd.CommandText = "GetRowNum";
            cmd.CommandType = System.Data.CommandType.StoredProcedure;
            cmd.Parameters.Add("@sort", System.Data.SqlDbType.NVarChar);
            cmd.Parameters["@sort"].Value = se;
            cmd.Parameters.Add("@table", System.Data.SqlDbType.NVarChar);
            cmd.Parameters["@table"].Value = table;
            cmd.Parameters.Add("@column", System.Data.SqlDbType.NVarChar);
            cmd.Parameters["@column"].Value = cl;
            cmd.Parameters.Add("@filter", System.Data.SqlDbType.NVarChar);
            cmd.Parameters["@filter"].Value = f;
            cmd.Parameters.Add("@SL", System.Data.SqlDbType.Int);
            cmd.Parameters["@SL"].Value = mr;
            cmd.Parameters.Add("@index", System.Data.SqlDbType.Int);
            cmd.Parameters["@index"].Value = si;

            SqlParameter pIdOut = new SqlParameter("@p_Count_out", System.Data.SqlDbType.Int);
            pIdOut.Direction = ParameterDirection.Output;
            cmd.Parameters.Add(pIdOut);



            DataTable dt = ExecuteProcedureData(cmd, _cs);
            int Total = 0;
            if ((pIdOut != null) && (pIdOut.Value != DBNull.Value))
                Total = (int)(pIdOut.Value);

            var list = new List<object[]>();
            for (var m = 0; m < dt.Rows.Count; m++) list.Add(dt.Rows[m].ItemArray);
            _r._d = list; _r._t = Total;
        }//end proc
        void IoU(string sql)
        {
            _r._d = -1;//fail
            using (var mC = new SqlConnection(_cs))
            {
                mC.Open();
                SqlTransaction mT = mC.BeginTransaction();      // Start a local transaction.
                SqlCommand mD = mC.CreateCommand();       // Enlist the command in the current transaction.
                mD.CommandTimeout = 36000;
                mD.Transaction = mT;
                try
                {
                    int curCount = 0;
                    if (_sqlL.Count > 0)
                        while (curCount < _sqlL.Count)
                        {
                            string ss = "";
                            int tmp = curCount;
                            while (curCount < tmp + 40 && curCount < _sqlL.Count)
                            {
                                ss += _sqlL[curCount]; curCount++;
                            }
                            if (ss.Length > 0)
                            {
                                mD.CommandText = ss; 
                                mD.ExecuteNonQuery();
                            }
                        }
                    mT.Commit();
                    _r._d = 1;//success
                }
                catch (Exception) { mT.Rollback(); _r._s = 0; _r._d = _E.E10009.G(); _r._d = -1; }
                finally { mD.Dispose(); mC.Close(); }
            }
        }
        private void I(string v)
        {
            var r = LI(_d); _sql = string.Format("INSERT {0} ({1}) VALUES ({2})", v, r[0], r[1]); _sqlL.Add(_sql);
        }
        private void U(string v)
        {
            _sql = string.Format("UPDATE {0} SET {1} {2}", v, LU(_d), LW(_c)); _sqlL.Add(_sql);   //{2} if WHERE is _c not null
        }
        private void R(string v)
        {
            _sql = string.Format("DELETE FROM {0} {1}", v, LW(_c)); _sqlL.Add(_sql);
        }
        private void G(string v)
        {
            _sql = string.Format("SELECT {0} FROM {1} {2}", _f, v, LW(_c)); _sqlL.Add(_sql);
        }

        /**/
        public static DataTable ExecuteProcedureData(SqlCommand cmd, string strConnect)
        {
            DataTable dt = new DataTable();
            using (SqlConnection myCon = new SqlConnection((strConnect)))
            {
                myCon.Open();
                SqlTransaction transaction;
                transaction = myCon.BeginTransaction(IsolationLevel.ReadCommitted, "Sample Transaction 0901");
                try
                {
                    cmd.CommandTimeout = 36000;
                    SqlDataAdapter dataAdapter = new SqlDataAdapter();
                    dataAdapter.SelectCommand = cmd;
                    dataAdapter.SelectCommand.Transaction = transaction;
                    dataAdapter.SelectCommand.Connection = myCon;
                    dataAdapter.Fill(dt);
                    transaction.Commit();
                }
                catch (Exception e)
                {
                    try
                    {
                        transaction.Rollback();
                    }
                    catch (SqlException ex)
                    {
                        if (transaction.Connection != null)
                        {
                            //NoneException(ex);
                        }
                    }
                    //NoneException(e);
                }
            }
            return dt;
        }
        /*-------------------------------------------------------------------------------*/
    }

}
